// @Author EthanScriptOn
// @Desc
package middleware

import (
	"context"
	"encoding/base64"
	"fmt"
	"gitee.com/fatzeng/srf_switch/cache"
	"gitee.com/fatzeng/srf_switch/config"
	"gitee.com/fatzeng/srf_switch/core/switch_subject"
	"github.com/spf13/cast"
	"strings"
	"time"
)

type SwitchSingleRequestDetection struct {
	switchCache        cache.Cache
	expiration         time.Duration
	singleRequestToken []string
}

func (s *SwitchSingleRequestDetection) GenerateSwitchSingleRequestDetection(switchCache cache.Cache, expiration time.Duration, singleRequestToken []string) *SwitchSingleRequestDetection {
	return &SwitchSingleRequestDetection{switchCache: switchCache, expiration: expiration, singleRequestToken: singleRequestToken}
}

func (s *SwitchSingleRequestDetection) PutCache(ctx context.Context, cacheItem interface{}, finalRuleState bool) {
	if !s.switchCache.IsEnabled() {
		return
	}
	key := s.GenerateCacheItemKey(ctx, cacheItem)
	_ = s.switchCache.Add(key, finalRuleState, s.expiration)
}

func (s *SwitchSingleRequestDetection) IsHitCache(ctx context.Context, cacheItem interface{}) (bool, bool) {
	if !s.switchCache.IsEnabled() {
		return false, false
	}
	hitCache, canHit := s.switchCache.Get(s.GenerateCacheItemKey(ctx, cacheItem))
	if hitCacheBool := hitCache.(bool); hitCacheBool && canHit {
		return canHit, hitCacheBool
	}
	return false, false
}

func (s *SwitchSingleRequestDetection) GenerateCacheItemPrefix() string {
	preBuilder := strings.Builder{}
	preBuilder.WriteString(config.SrfSwitchGlobalConfig.SrfLocatorConfig.SrfNamespace)
	preBuilder.WriteString(config.SrfSwitchGlobalConfig.SrfLocatorConfig.SrfGroup)
	preBuilder.WriteString(config.SrfSwitchGlobalConfig.SrfEnvConfig.SrfEnv)
	return preBuilder.String()
}

func (s *SwitchSingleRequestDetection) GenerateCacheItemKey(ctx context.Context, cacheItem interface{}) string {
	keyBuilder := strings.Builder{}
	keyBuilder.WriteString(s.GenerateCacheItemPrefix())
	keyBuilder.WriteString(s.structureRequestToken(ctx))
	switch item := cacheItem.(type) {
	case *switch_subject.FactorRule:
		keyBuilder.WriteString(cast.ToString(item.FactorId))
	case *switch_subject.SrfRule:
		keyBuilder.WriteString(cast.ToString(item.Relationship))
	}
	return base64.StdEncoding.EncodeToString([]byte(keyBuilder.String()))
}

func (s *SwitchSingleRequestDetection) structureRequestToken(ctx context.Context) string {
	rtBuilder := strings.Builder{}
	for _, rt := range s.singleRequestToken {
		rtBuilder.WriteString(rt)
		rtBuilder.WriteString(":")
		rtBuilder.WriteString(fmt.Sprint(ctx.Value(rt)))
	}
	return rtBuilder.String()
}

func (s *SwitchSingleRequestDetection) Clear() {
	s.switchCache.Clear()
}
